// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#include "rotor-light/actor.hpp"
#include "misc.h"
#include "rotor-light/planner.hpp"
#include "rotor-light/queue.hpp"
#include "rotor-light/supervisor.hpp"
#include <new>

namespace rotor_light {

using Command = details::StateCommand;

ActorBase::ActorBase(char *backends, size_t backends_count_)
    : id{0}, fail_policy{FailPolicy::escalate}, backends_ptr{backends},
      backends_count{backends_count_}, backend_idx{-1} {}

void ActorBase::initialize() { subscribe(&ActorBase::on_state_change); }

void ActorBase::on_state_change(message::ChangeState &message) {
  assert(message.to == id);
  auto cmd = message.command;
  if (cmd == Command::init) {
    assert(state == State::off);
    state = State::initializing;
    advance_init();
  } else if (cmd == Command::start) {
    assert(state == State::initialized);
    state = State::operational;
    advance_start();
  } else if (cmd == Command::shutdown) {
    if ((state != State::off) && (state != State::shutting_down)) {
      state = State::shutting_down;
      advance_stop(message.success);
    }
  }
}

void ActorBase::advance_init() {
  state = State::initialized;
  bool ok = send<ctx::thread, message::ChangeStateAck>(
      default_queue, supervisor->id, id, State::initialized, true);
  if (!ok) {
    on_queue_full();
  }
}

void ActorBase::advance_start() {}

void ActorBase::advance_stop(bool success) {
  state = State::off;
  bool ok = send<ctx::thread, message::ChangeStateAck>(
      default_queue, supervisor->id, id, State::off, success);
  if (!ok) {
    on_queue_full();
  }
  backend_idx = -1;
}

uint8_t ActorBase::bind(ActorId initial_value, SupervisorBase *supervisor,
                        Context &) {
  assert(initial_value);
  this->supervisor = supervisor;
  id = mask = initial_value;
  return 1;
}

void ActorBase::stop(bool success) {
  if (state != State::off && state != State::shutting_down) {
    auto ok = send<ctx::thread, message::ChangeState>(
        default_queue, id, Command::shutdown, success);
    if (!ok) {
      on_queue_full();
    }
  }
}

void ActorBase::cancel_event(EventId event_id) {
  supervisor->planner->remove_event(event_id);
}

} // namespace rotor_light
